package week04;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.Phaser;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

public class Main {

    private static volatile boolean stop;
    private static final Object lock = new Object();
    private static final ReentrantLock enLock = new ReentrantLock();
    private static final Condition condition = enLock.newCondition();

    public static void main(String[] args) {
        Object result =
//        runJoin();            // 1
//        runVolatile();        // 2
//        runCountDownLatch();  // 3
//        runCyclicBarrier();   // 4
//        runCompleteFuture();  // 5
//        runIsAlive();         // 6
//        runWaitNotify();      // 7
//        runCompletable();     // 8
//        runSleep();           // 9
//        runPark();            // 10
//        runForkJoin();        // 11
//        runBlockingQueue();   // 12
//        runExchanger();       // 13
//        runPhaser();          // 14
//        runReentrantLock();   // 15
        runInterrupt();       // 16
        System.out.println("Main Thread, get fibo result=" + result);
    }

    private static Object runJoin() {
        ResultThread t = new SubTask();
        t.start();
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return t.getResult();
    }

    private static Object runVolatile() {
        ResultThread t = new VolatileSubTask();
        t.start();
        while (!stop) {
            Thread.yield();
        }
        return t.getResult();
    }

    private static Object runCountDownLatch() {
        CountDownLatch latch = new CountDownLatch(1);
        ResultThread t = new CountDownTask(latch);
        t.start();
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return t.getResult();
    }

    private static Object runCyclicBarrier() {
        CyclicBarrier barrier = new CyclicBarrier(2);
        ResultThread t = new CyclicBarrierTask(barrier);
        t.start();
        try {
            barrier.await();
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
        return t.getResult();
    }

    private static Object runCompleteFuture() {
        ExecutorService service = Executors.newSingleThreadExecutor();
        try {
            return service.submit(() -> {
                ResultThread t = new SubTask();
                t.run();
                return t.getResult();
            }).get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            service.shutdown();
        }
        throw new IllegalStateException();
    }

    private static Object runSleep() {
        ResultThread task = new SubTask();
        task.start();
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return task.getResult();
    }

    private static Object runCompletable() {
        CompletableFuture<Object> future = CompletableFuture.supplyAsync(() -> {
            ResultThread t = new SubTask();
            t.run();
            return t.getResult();
        });
        return future.join();
    }

    private static Object runWaitNotify() {
        ResultThread t = new ResultThread() {
            @Override
            public void run() {
                ResultThread task = new SubTask();
                task.run();
                setResult(task.getResult());
                synchronized (lock) {
                    lock.notify();
                }
            }
        };
        t.start();
        try {
            synchronized (lock) {
                lock.wait(1000); // whit timeout args which in case, async task get that lock first
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return t.getResult();
    }

    private static Object runIsAlive() {
        ResultThread t = new SubTask();
        t.start();
        while (t.isAlive()) {
            Thread.yield();
        }
        return t.getResult();
    }

    private static Object runPark() {
        final Thread mainThread = Thread.currentThread();
        ResultThread t = new ParkTask(mainThread);
        t.start();
        LockSupport.park(lock);
        return t.getResult();
    }

    private static Object runForkJoin() {
        ForkJoinPool pool = new ForkJoinPool();
        try {
            ForkJoinTask<Object> task = pool.submit(new ForkJoinSubTask());
            return task.join();
        } finally {
            pool.shutdown();
        }
    }

    private static Object runBlockingQueue() {
        final BlockingQueue<Object> queue = new ArrayBlockingQueue<>(1);
        new Thread(() -> {
            ResultThread t = new SubTask();
            t.run();
            try {
                queue.put(t.getResult());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        try {
            return queue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        throw new IllegalStateException();
    }

    private static Object runExchanger() {
        Exchanger<Object> exchanger = new Exchanger<>();
        new Thread(() -> {
            ResultThread t = new SubTask();
            t.run();
            try {
                exchanger.exchange(t.getResult());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        try {
            return exchanger.exchange(null);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        throw new IllegalStateException();
    }

    private static Object runPhaser() {
        Phaser phaser = new Phaser(2);
        ResultThread t = new PhaserTask(phaser);
        t.start();
        phaser.arriveAndAwaitAdvance();
        return t.getResult();
    }

    private static Object runReentrantLock() {
        ResultThread t = new ResultThread() {
            @Override
            public void run() {
                enLock.lock();
                try {
                    ResultThread task = new SubTask();
                    task.run();
                    setResult(task.getResult());
                    condition.signal();
                } finally {
                    enLock.unlock();
                }
            }
        };
        t.start();
        enLock.lock();
        try {
            condition.await(1000, TimeUnit.MILLISECONDS); // whit timeout args which in case, async task get that lock first
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            enLock.unlock();
        }
        return t.getResult();
    }

    private static Object runInterrupt() {
        final Thread main = Thread.currentThread();
        ResultThread t = new ResultThread() {
            @Override
            public void run() {
                ResultThread task = new SubTask();
                task.run();
                setResult(task.getResult());
                main.interrupt();
            }
        };
        t.start();
        try {
            while (!main.isInterrupted()) {
                TimeUnit.SECONDS.sleep(200); // 睡眠 200s
            }
        } catch (InterruptedException e) {
            // ignore（被主动中断）
        }
        return t.getResult();
    }

    static class ForkJoinSubTask extends RecursiveTask<Object> {

        @Override
        protected Object compute() {
            ResultThread t = new SubTask();
            t.run();
            return t.getResult();
        }
    }

    static class SubTask extends ResultThread {
        @Override
        public void run() {
            long start = System.currentTimeMillis();
            setResult(sum());
            // 确保  拿到result 并输出
            System.out.println("异步计算结果为：" + getResult());
            System.out.println("使用时间：" + (System.currentTimeMillis() - start) + "ms");
        }
        private int sum() {
            return fibo(36);
        }
        private int fibo(int a) {
            if (a < 2)
                return 1;
            return fibo(a - 1) + fibo(a - 2);
        }
    }

    static class ParkTask extends ResultThread {
        Thread main;
        ParkTask(Thread main) {
            this.main = main;
        }
        @Override
        public void run() {
            ResultThread t = new SubTask();
            t.run();
            setResult(t.getResult());
            LockSupport.unpark(main);
        }
    }

    static class VolatileSubTask extends ResultThread {
        @Override
        public void run() {
            SubTask task = new SubTask();
            task.run();
            setResult(task.getResult());
            stop = true;
        }
    }

    static class CountDownTask extends ResultThread {
        private final CountDownLatch latch;

        CountDownTask(CountDownLatch latch) {
            this.latch = latch;
        }

        @Override
        public void run() {
            ResultThread t = new SubTask();
            t.run();
            setResult(t.getResult());
            latch.countDown();
        }
    }

    static class CyclicBarrierTask extends ResultThread {
        private final CyclicBarrier barrier;

        CyclicBarrierTask(CyclicBarrier barrier) {
            this.barrier = barrier;
        }

        @Override
        public void run() {
            try {
                ResultThread t = new SubTask();
                t.run();
                setResult(t.getResult());
                barrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    static class PhaserTask extends ResultThread {
        Phaser phaser;
        PhaserTask(Phaser phaser) {
            this.phaser = phaser;
        }
        @Override
        public void run() {
            ResultThread t = new SubTask();
            t.run();
            setResult(t.getResult());
            phaser.arriveAndAwaitAdvance();
        }
    }

    static class ResultThread extends Thread {
        private Object result;

        public ResultThread() {
        }
        public ResultThread(Runnable runnable) {
            super(runnable);
        }
        public Object getResult() {
            return result;
        }
        public void setResult(Object result) {
            this.result = result;
        }
        @Override
        public void run() {
            super.run();
        }
    }
}
